<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>闭包</title>
    <style>
      button {
        margin-right: 10px;
      }
    </style>
  </head>
  <body>
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>

    <script>
      /* 
        1.如何产生闭包？
        当一个嵌套的内部（子）函数引用了嵌套的外部（父）函数的变量（函数）时，就产生了闭包
        
        2.闭包到底是什么？
        使用chrome调试查看
        理解一：闭包是嵌套的内部函数（绝大部分人）
        理解二：包含被引用变量（函数）的对象（极少数人）
        注意：闭包存在于嵌套的内部函数中

        3.产生闭包的条件？
        函数嵌套
        内部函数引用了外部函数的数据（变量/函数）
        执行函数定义的时候就会产生闭包


        -----------------------
        1.将函数作为另一个函数的返回值
        2.将函数作为实参传递给另一个函数调用



        --------------------------------闭包的作用--------------------------
        1. 使用函数内部的变量在函数执行完后，仍然存活在内存中（延长了局部变量的生命周期）
        2. 让函数外部可以操作（读写）到函数内部的数据（变量/函数）

        问题：
        1. 函数执行完后，函数内部声明的局部变量是否还存在？一般不存在，存在于闭包中的变量才可能存在
        2. 在函数外部能直接访问函数内部的局部变量吗？不能，但可以通过闭包让外部操作它


        --------------------闭包的生命周期------------------------------------
        1.产生：在嵌套内部函数定义执行完时就产生了（不是在调用）
        2.死亡：在嵌套的内部函数称为垃圾对象时


        ---------闭包的应用----------------
        自定义JS模块
        具有特定功能的js文件
        将所有的数据和功能都封装在一个函数内部（私有的）
        只向外暴露一个包含n个方法的对象或函数
        模块的使用者，只需要通过模块暴露的对象调用方法来实现对应的功能

        -----------------------------------
        1.缺点
        函数执行完后，函数内的局部变量没有释放，占用内存事件会变长
        容易造成内存泄露

        2.解决
        能不用闭包就不用
        及时释放
        */

      /* var btns = document.getElementsByTagName("button");
      for (var i = 0, len = btns.length; i < len; i++) {
        const btn = btns[i];
        btn.index = i + 1;
        btn.onclick = function () {
          alert(`第${this.index}个`);
        };
      } */
      // 1.将函数作为另一个函数的返回值
      function fn1() {
        var a = 2;
        function fn2() {
          a++;
          console.log(a);
        }

        return fn2;
      }

      var f = fn1();

      f();
      f();

      // 2.将函数作为实参传递给另一个函数调用
      /* function showDelay(msg, time) {
        setTimeout(function () {
          alert(msg);
        }, time);
      }

      showDelay("xxxx", 200); */
    </script>
  </body>
</html>
